#include "DXUT.h"
#include "Regionmap.h"

#ifdef MULTILOAD
Regionmap::Regionmap(bool loadFiles) {
	InitializeSRWLock(&LOCK);
};

Regionmap::~Regionmap() {

};

size_t Regionmap::size() {
	return m_map.size();
}

Region * Regionmap::getRegion(int x, int z) {
	Position Pos	= Position(x, z);
	Region * result = NULL;

	AcquireSRWLockShared(&LOCK);
	auto it = m_map.find(Pos);
	if (it != m_map.end()) {
		result = it->second.region;
	}
	ReleaseSRWLockShared(&LOCK);

	return result;
};

Region * Regionmap::removeRegion(int x, int z) {
	Position Pos	= Position(x, z);
	Region * result = NULL;

	AcquireSRWLockExclusive(&LOCK);
	auto it = m_map.find(Pos);
	if (it != m_map.end()) {
		if (it->second.subtract()) {
			result = it->second.region;
			m_map.erase(it);
		}
	}
	ReleaseSRWLockExclusive(&LOCK);

	return result;
};

Region * Regionmap::insertRegion(Region * region, int x, int z) {
	Position Pos	= Position(x, z);
	Region * result = region;

	AcquireSRWLockExclusive(&LOCK);
	auto it = m_map.find(Pos);
	if (it != m_map.end()) {
		it->second.add();
		result = it->second.region;
		ReleaseSRWLockShared(&LOCK);
		return result;
	} else {
		m_map.insert(Region_type(Pos, region));
	}
	ReleaseSRWLockExclusive(&LOCK);

	return result;
};

Region * Regionmap::createRegion(IWorld* world, int seed, int x, int z) {
	Position Pos	= Position(x, z);
	Region * result = NULL;//new Region(world, seed, x, z);

	AcquireSRWLockExclusive(&LOCK);
	auto it = m_map.find(Pos);
	if (it != m_map.end()) {
		it->second.add();
		result = it->second.region;
	} else {
		result = new Region(world, seed, x, z);
		Regioncombound rgn = Regioncombound(result);
		m_map.insert(Region_type(Pos, rgn));
	}
	ReleaseSRWLockExclusive(&LOCK);

	return result;
};

Region * Regionmap::getValue(int i) {
	Region * result = NULL;

	AcquireSRWLockShared(&LOCK);
	for(auto it = m_map.begin(); (i >= 0) && (it != m_map.end()); it++, i--) {
		if (i == 0)
			result = it->second.region;
	}
	ReleaseSRWLockShared(&LOCK);

	return result;
}

/* Load if True returns */
int Regionmap::loadChunk(int x, int z, BYTE ix, BYTE iz) {
	Position Pos	= Position(x, z);
	int result		= -1;

	AcquireSRWLockShared(&LOCK);
	auto it = m_map.find(Pos);
	if (it != m_map.end()) {		
		if ((it->second.getLoadedChunks(ix,iz) == 0))
			result = 0;
		else
			result = 1;
		it->second.add(ix,iz);
	}
	ReleaseSRWLockShared(&LOCK);

	if (result == 0)
		result = it->second.region->loadCluster(ix, iz);
	return result;
};

/* Unloaded if True returns */
Region * Regionmap::unloadChunk(int x, int z, BYTE ix, BYTE iz, Chunk ** chunks) {
	Position Pos	= Position(x, z);
	Region * result	= NULL;

	AcquireSRWLockExclusive(&LOCK);
	auto it = m_map.find(Pos);
	if (it != m_map.end()) {		
		if (it->second.subtract(ix,iz)) {
			result = it->second.region;
			if (it->second.countChunks == 0)
				m_map.erase(it);
		}
	}
	ReleaseSRWLockExclusive(&LOCK);

	if (result) {
		bool empty = result->unloadCluster(ix, iz, chunks);
		if (empty) {					

		} else {
			result = NULL;
		}
	}

	return result;
};
#else
Regionmap::Regionmap(bool loadFiles) {
	m_loadFiles = loadFiles;
	m_size		= 32;
	m_map		= (Region**)calloc(m_size, sizeof(Region*));
	m_pos		= (Position*)calloc(m_size, sizeof(Position));
	ZeroMemory(m_map, m_size * sizeof(Region*));
	ZeroMemory(m_pos, m_size * sizeof(Position));
};

Regionmap::~Regionmap() {
	free(m_map);
	free(m_pos);
};

Region * Regionmap::getRegion(int x, int z) {
	Position pos = Position(x, z);
	for (int i = 0; i < m_size; i++) {
		if (m_pos[i] == pos)
			return m_map[i];
	}
	return NULL;
};

Region * Regionmap::removeRegion(int x, int z) {
	Position pos = Position(x, z);
	for (int i = 0; i < m_size; i++) {
		if (m_pos[i] == pos) {
			m_pos[i].loaded = false;
			return m_map[i];
		}
	}
	return NULL;
};

Region * Regionmap::insertRegion(Region * region, int x, int z) {
	Position pos = Position(x, z);
	for (int i = 0; i < m_size; i++) {
		if (m_pos[i] == pos)
			return m_map[i];
	}
	for (int i = 0; i < m_size; i++) {
		if (!m_pos[i].loaded) {
			m_map[i] = region;
			m_pos[i] = pos;
			return m_map[i];
		}
	}
	return NULL;
};

Region * Regionmap::createRegion(IWorld* world, int seed, int x, int z) {
	Position pos = Position(x, z);
	for (int i = 0; i < m_size; i++) {
		if (m_pos[i] == pos)
			return m_map[i];
	}
	for (int i = 0; i < m_size; i++) {
		if (!m_pos[i].loaded) {
			m_map[i] = new Region(world, seed, x, z, m_loadFiles);
			m_pos[i] = Position(x, z);
			return m_map[i];
		}
	}
	return NULL;
};

Region * Regionmap::getValue(int i) {
	if (m_pos[i].loaded)
		return m_map[i];
	else
		return NULL;
};

size_t Regionmap::size() {	
	return m_size;
};

int Regionmap::loadChunk(int x, int z, BYTE ix, BYTE iz) {
	Position pos = Position(x, z);
	for (int i = 0; i < m_size; i++) {
		if (m_pos[i] == pos)
			return m_map[i]->loadCluster(ix, iz);
	}
	return 0;
};

Region * Regionmap::unloadChunk(int x, int z, BYTE ix, BYTE iz, Chunk ** chunks) {
	Position pos = Position(x, z);
	for (int i = 0; i < m_size; i++) {
		if (m_pos[i] == pos)
			if (m_map[i]->unloadCluster(ix, iz, chunks)) {
				m_pos[i].loaded = false;
				return m_map[i];
			} else {
				return NULL;
			}
	}
	return NULL;
};


#ifdef CLIENT
ClientRegionmap::ClientRegionmap(ClientWorld * pWorld, bool loadFiles) : Regionmap(loadFiles) {
	m_pWorld = pWorld;
};

Region * ClientRegionmap::createRegion(ClientWorld* world, int seed, int x, int z) {
	Position pos = Position(x, z);
	for (int i = 0; i < m_size; i++) {
		if (m_pos[i] == pos)
			return m_map[i];
	}
	for (int i = 0; i < m_size; i++) {
		if (!m_pos[i].loaded) {
			m_map[i] = new Region(world, seed, x, z);
			m_pos[i] = Position(x, z);
			return m_map[i];
		}
	}
	return NULL;
};

int ClientRegionmap::loadChunk(int x, int z, BYTE ix, BYTE iz) {
	Position pos = Position(x, z);
	for (int i = 0; i < m_size; i++) {
		if (m_pos[i] == pos) {
			return 
				m_map[i]->loadCluster(ix, iz);
		}
	}
	return 0;
};
#endif

Regioncombound::Regioncombound() {
	InitializeSRWLock(&LOCK);
	region		= NULL;
	count		= 0;
	countChunks = 0;
	ZeroMemory(loadedCunks, 256);
};

Regioncombound::Regioncombound(Region * _region) {
	InitializeSRWLock(&LOCK);
	region		= _region;
	count		= 1;
	countChunks = 0;
	ZeroMemory(loadedCunks, 256);
};

bool Regioncombound::subtract(){
	bool result = false;
	AcquireSRWLockExclusive(&LOCK);
	if (count <= 1) {
		count = 0;
		result =  true;
	} else {
		count--;
	}
	ReleaseSRWLockExclusive(&LOCK);

	return result;
};

void Regioncombound::add() {
	AcquireSRWLockExclusive(&LOCK);
	count++;
	ReleaseSRWLockExclusive(&LOCK);
};

bool Regioncombound::subtract(BYTE _x, BYTE _z) {
	AcquireSRWLockExclusive(&LOCK);
	if (countChunks <= 1) {
		countChunks = 0;
	} else {
		countChunks--;
	}

	_z <<= 4;
	bool result = false;
	if (loadedCunks[_x + _z] <= 1) {
		loadedCunks[_x + _z] = 0;
		result =  true;
	} else {
		loadedCunks[_x + _z]--;
	}
	ReleaseSRWLockExclusive(&LOCK);

	return result;
};

void Regioncombound::add(BYTE _x, BYTE _z) {
	AcquireSRWLockExclusive(&LOCK);
	loadedCunks[_x + (_z << 4)]++;
	countChunks++;
	ReleaseSRWLockExclusive(&LOCK);
};

BYTE Regioncombound::getLoadedChunks(BYTE _x, BYTE _z) {
	AcquireSRWLockShared(&LOCK);
	BYTE result = loadedCunks[_x + (_z << 4)];
	ReleaseSRWLockShared(&LOCK);

	return result;
};

#endif